# _*_coding:utf-8_*_
'''
Created on Mar 7, 2014
Currently only for model 6501. The module would be enhanced for other models in
future.
@author: JZHAI
'''
import ctypes
from ATS.errHandler import errHandler
from ATS.instrumentDriver import InstGen
from ATS.instrumentDriver import InstConstant as const
try:
    dio_dll = ctypes.windll.LoadLibrary("nicaiu.dll")
except:
    dio_dll = None
# from ATS.instrumentDriver import MyInstGen



TASK_NOT_CREATE = 30001


class DIO_NI(InstGen.DIO):
    def __init__(self, name, info_dict):
        super().__init__(name, info_dict)

        # since only one task can run at once, here handler will be updated when \
        # each time CreateTask() called.
        self.handler = {}
        self.dioinfo = self.getdiospec()

    def __gethandler(self):
        return self.handler

    def __posterr(self, msg):
        who = "instrumentDriver.InstGen.DIO.DIO_NI"
        err_code = 30000
        print("****", who, err_code, msg)
        errHandler.handle_err(who, err_code, msg)

    def getdiospec(self):
        dioinfo = {}
        if self.info_dict["model"] == "6501":
            dioinfo["port_cnt"] = 3
            dioinfo["port_width"] = 8
        return dioinfo

    def getlineconfig(self):
        lineconfig = {}
        use_line = []
        for k in self.info_dict:
            if k.startswith("line"):
                v = self.info_dict[k]
                l_ind = int(k[4:])
                lineconfig[l_ind] = v
                use_line.append(l_ind)
        use_line.sort()
        lineconfig["use_line"] = use_line
        self.lineconfig = lineconfig
        return lineconfig

    def GetErrorString(self, err_code):
        """
        int32 DAQmxGetErrorString (int32 errorCode, char errorString[], uInt32 bufferSize);
        """
        global dio_dll
        errorCode = ctypes.c_int32(err_code)
        errorString = ctypes.create_string_buffer("", 1000)
        bufferSize = ctypes.c_uint32(1000)
        stat = dio_dll.DAQmxGetErrorString(errorCode, ctypes.byref(errorString), bufferSize)
        # print("GetErrorString stat", stat)
        return repr(errorString.value)

    def CreateTask(self, task_name=""):
        """
        int32 DAQmxCreateTask (const char taskName[], TaskHandle *taskHandle);
        """
        global dio_dll
        task_name_c = ctypes.c_char_p(task_name)
        task_handle = ctypes.c_uint32(0)
        stat = dio_dll.DAQmxCreateTask(task_name_c, ctypes.byref(task_handle))
        if stat != 0:
            err_str = self.GetErrorString(stat)
            self.__posterr("CreateTask() function. " + err_str)
            return stat
        self.handler[task_name] = task_handle
        return task_handle

    def TaskControl(self, action, task_name):
        """
        int32 DAQmxTaskControl (TaskHandle taskHandle, int32 action);
        """
        global dio_dll
        taskHandle = self.handler["task_name"]
        action = ctypes.c_int32(action)
        stat = dio_dll.DAQmxTaskControl(taskHandle, action)
        if stat != 0:
            err_str = self.GetErrorString(stat)
            self.__posterr("TaskControl() function. " + err_str)
            return stat
        return stat

    def CreateDOChan(self, chan_name, task_name="dout"):
        """
        chan_name(DIO channel name):["port0/line0:2","port1/line1","port0:1","line0:31"]
        int32 DAQmxCreateDOChan (TaskHandle taskHandle, const char lines[], const char nameToAssignToLines[], int32 lineGrouping);
        """
        global dio_dll
        dev_id = self.info_dict.get("dev_num", 1)
        chan_name = ["dev%d/" % dev_id + s for s in chan_name]
        chan_name = ",".join(chan_name)
        lines = ctypes.c_char_p(chan_name)
        nameToAssignToLines = ctypes.c_char_p("")
        lineGrouping = ctypes.c_ushort(const.DAQmx_Val_ChanForAllLines)
        if task_name not in self.handler:
            err_str = "Task %s isn't created." % task_name
            self.__posterr("CreateDOChan() function. " + err_str)
            return TASK_NOT_CREATE
        stat = dio_dll.DAQmxCreateDOChan(self.handler[task_name], lines, nameToAssignToLines, lineGrouping)
        if stat != 0:
            err_str = self.GetErrorString(stat)
            self.__posterr("CreateDOChan() function. " + err_str)
            return stat
        return stat

    def CreateCICountEdgesChan(self, chan_name="ctr0", task_name="counter"):
        """
        edge = DAQmx_Val_Falling  P2.7 can be configured as a counter, which only can \
            monitor the signal from high to low, it is falling edge.
        countDirection = DAQmx_Val_CountUp
        int32 DAQmxCreateCICountEdgesChan (TaskHandle taskHandle, const char counter[], const char nameToAssignToChannel[], int32 edge, uInt32 initialCount, int32 countDirection);
        """
        global dio_dll
        dev_id = self.info_dict.get("dev_num", 1)
        counter = "dev%d/" % dev_id + chan_name  # only one counter channel supported for one task
        counter = ctypes.c_char_p(counter)
        # print("****",counter)
        nameToAssignToChannel = ctypes.c_char_p("")
        edge = ctypes.c_int32(const.DAQmx_Val_Falling)
        initialCount = ctypes.c_uint32(0)
        countDirection = ctypes.c_int32(const.DAQmx_Val_CountUp)
        if task_name not in self.handler:
            err_str = "Task %s isn't created." % task_name
            self.__posterr("CreateCICountEdgesChan() function. " + err_str)
            return TASK_NOT_CREATE
        stat = dio_dll.DAQmxCreateCICountEdgesChan(self.handler[task_name], counter, nameToAssignToChannel, edge,
                                                   initialCount, countDirection)
        if stat != 0:
            err_str = self.GetErrorString(stat)
            self.__posterr("CreateCICountEdgesChan() function. " + err_str)
            return stat
        return stat

    def StartTask(self, task_name):
        """
        int32 DAQmxStartTask (TaskHandle taskHandle);
        """
        global dio_dll
        if task_name not in self.handler:
            err_str = "Task %s isn't created." % task_name
            self.__posterr("StartTask() function. " + err_str)
            return TASK_NOT_CREATE
        stat = dio_dll.DAQmxStartTask(self.handler[task_name])
        if stat != 0:
            err_str = self.GetErrorString(stat)
            self.__posterr("StartTask() function. " + err_str)
            return stat
        return stat

    def StopTask(self, task_name):
        """
        int32 DAQmxStopTask (TaskHandle taskHandle);
        """
        global dio_dll
        if task_name not in self.handler:
            err_str = "Task %s isn't created." % task_name
            self.__posterr("StopTask() function. " + err_str)
            return TASK_NOT_CREATE
        stat = dio_dll.DAQmxStopTask(self.handler[task_name])
        if stat != 0:
            err_str = self.GetErrorString(stat)
            # __posterr("StopTask() function. "+err_str)
            return stat
        return stat

    def ClearTask(self, task_name):
        """
        int32 DAQmxClearTask (TaskHandle taskHandle);
        """
        global dio_dll
        if task_name not in self.handler:
            err_str = "Task %s isn't created." % task_name
            self.__posterr("ClearTask() function. " + err_str)
            return TASK_NOT_CREATE
        stat = dio_dll.DAQmxClearTask(self.handler[task_name])
        if stat != 0:
            err_str = self.GetErrorString(stat)
            self.__posterr("ClearTask() function. " + err_str)
            return stat
        self.handler.pop(task_name)
        return stat

    def ReadCounterScalarU32(self, timeout=0, task_name="counter"):
        """
        int32 DAQmxReadCounterScalarU32 (TaskHandle taskHandle, \
            float64 timeout, uInt32 *value, bool32 *reserved);
        """
        global dio_dll
        timeout = ctypes.c_double(timeout)
        reserved = None
        value = ctypes.c_uint32(0)
        if task_name not in self.handler:
            err_str = "Task %s isn't created." % task_name
            self.__posterr("ReadCounterScalarU32() function. " + err_str)
            return TASK_NOT_CREATE
        stat = dio_dll.DAQmxReadCounterScalarU32(self.handler[task_name], timeout, \
                                                 ctypes.byref(value), reserved)
        if stat != 0:
            err_str = self.GetErrorString(stat)
            self.__posterr("ReadCounterScalarU32() function. " + err_str)
            return stat
        return value.value

    def WriteDigitalLines(self, write_list, task_name="dout"):
        """
        When you create your write array, each sample per channel must contain \
        the number of bytes returned by the DAQmx_Write_DigitalLines_BytesPerChan\
         property.(Currently no check)
        int32 DAQmxWriteDigitalLines (TaskHandle taskHandle, \
            int32 numSampsPerChan, bool32 autoStart, float64 timeout, \
            bool32 dataLayout, uInt8 writeArray[], int32 *sampsPerChanWritten,\
             bool32 *reserved);
        """
        global dio_dll
        numSampsPerChan = ctypes.c_int32(1)
        autoStart = ctypes.c_ushort(0)
        timeout = ctypes.c_double(10)
        dataLayout = ctypes.c_ushort(const.DAQmx_Val_GroupByChannel)
        writeArray = (ctypes.c_uint8 * len(write_list))(*write_list)
        sampsPerChanWritten = ctypes.c_int32(0)
        reserved = None
        if task_name not in self.handler:
            err_str = "Task %s isn't created." % task_name
            self.__posterr("WriteDigitalLines() function. " + err_str)
            return TASK_NOT_CREATE
        stat = dio_dll.DAQmxWriteDigitalLines(self.handler[task_name], numSampsPerChan,
                                              autoStart, timeout,
                                              dataLayout, writeArray,
                                              ctypes.byref(sampsPerChanWritten),
                                              reserved)
        if stat != 0:
            err_str = self.GetErrorString(stat)
            self.__posterr("WriteDigitalLines() function. " + err_str)
            return stat
        return stat
